 /*******************************************************************************
  * Copyright (c) 2004, 2007 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/

 package org.eclipse.ui.internal.intro.impl.model;

 import org.eclipse.core.runtime.IRegistryChangeEvent;
 import org.eclipse.core.runtime.PerformanceStats;
 import org.eclipse.jface.action.Action;
 import org.eclipse.jface.action.IToolBarManager;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.ui.IActionBars;
 import org.eclipse.ui.IMemento;
 import org.eclipse.ui.dialogs.ElementTreeSelectionDialog;
 import org.eclipse.ui.internal.intro.impl.IIntroConstants;
 import org.eclipse.ui.internal.intro.impl.IntroPlugin;
 import org.eclipse.ui.internal.intro.impl.Messages;
 import org.eclipse.ui.internal.intro.impl.model.viewer.IntroModelContentProvider;
 import org.eclipse.ui.internal.intro.impl.model.viewer.IntroModelLabelProvider;
 import org.eclipse.ui.internal.intro.impl.util.ImageUtil;
 import org.eclipse.ui.internal.intro.impl.util.Log;
 import org.eclipse.ui.internal.intro.impl.util.Util;
 import org.eclipse.ui.intro.IIntroPart;
 import org.eclipse.ui.intro.config.CustomizableIntroPart;

 /**
  * UI Implementation class that represents a Presentation Part. This class is
  * instantiated from plugin markup and so we need a default constructor,
  * including in subclasses. It has some base methods, including maintaining a
  * history of navigation locations. Also, dynamic awarness is honored here.
  *
  */
 public abstract class AbstractIntroPartImplementation {

     // CustomizableIntroPart instance.
 private CustomizableIntroPart introPart = null;

     // IMemento for restoring state.
 private IMemento memento;

     protected History history = new History();

     // flag used to enable logging of perf data for full UI creation only once.
 // Since standbyStateChanged is called several times, flag is used in method
 // to filter out all subsequent calls.
 boolean logUIcreationTime = true;

     // Global actions
 protected Action backAction = new Action() {

         {
             setToolTipText(Messages.Browser_backwardButton_tooltip);
             setImageDescriptor(ImageUtil
                 .createImageDescriptor("full/elcl16/backward_nav.gif")); //$NON-NLS-1$
 setDisabledImageDescriptor(ImageUtil
                 .createImageDescriptor("full/dlcl16/backward_nav.gif")); //$NON-NLS-1$
 }

         public void run() {
             navigateBackward();
         }
     };

     protected Action forwardAction = new Action() {

         {
             setToolTipText(Messages.Browser_forwardButton_tooltip);
             setImageDescriptor(ImageUtil
                 .createImageDescriptor("full/elcl16/forward_nav.gif")); //$NON-NLS-1$
 setDisabledImageDescriptor(ImageUtil
                 .createImageDescriptor("full/dlcl16/forward_nav.gif")); //$NON-NLS-1$
 }

         public void run() {
             navigateForward();
         }
     };

     protected Action homeAction = new Action() {

         {
             setToolTipText(Messages.Browser_homeButton_tooltip);
             setImageDescriptor(ImageUtil
                 .createImageDescriptor("full/elcl16/home_nav.gif")); //$NON-NLS-1$
 setDisabledImageDescriptor(ImageUtil
                 .createImageDescriptor("full/dlcl16/home_nav.gif")); //$NON-NLS-1$
 }

         public void run() {
             navigateHome();
         }
     };

     protected Action viewIntroModelAction = new Action() {

         {
             setToolTipText(Messages.IntroPart_showContentButton_tooltip);
             setImageDescriptor(ImageUtil
                 .createImageDescriptor("contents_view.gif")); //$NON-NLS-1$
 }

         public void run() {
             ElementTreeSelectionDialog treeViewer = new ElementTreeSelectionDialog(
                 getIntroPart().getIntroSite().getShell(),
                 new IntroModelLabelProvider(), new IntroModelContentProvider());
             treeViewer.setInput(getModel());
             treeViewer.open();
         }
     };

     /**
      * Creates the UI based on this implementation class. .
      *
      * @param parent
      */
     public abstract void createPartControl(Composite parent);

     /**
      * Called when the init method is called in the IIntroPart. Subclasses may
      * extend, for example to get the passed Memento. When extending, make sure
      * you include a call to super.
      *
      * @param introPart
      */
     public void init(IIntroPart introPart, IMemento memento) {
         // we know the class type to cast to.
 this.introPart = (CustomizableIntroPart) introPart;
         this.memento = memento;
     }

     /**
      * @return
      */
     public IntroModelRoot getModel() {
         return IntroPlugin.getDefault().getIntroModelRoot();
     }

     /**
      * @return Returns the introPart.
      */
     public CustomizableIntroPart getIntroPart() {
         return introPart;
     }


     /**
      * Updates the UI navigation history with either a real URL.
      *
      * @param location
      */
     public void updateHistory(String location) {
         history.updateHistory(location);
         updateNavigationActionsState();
     }

     /**
      * Updates the UI navigation history with a page ID.
      *
      * @param pageId
      */
     public void updateHistory(AbstractIntroPage page) {
         history.updateHistory(page);
         updateNavigationActionsState();
     }


     /**
      * Subclasses must implement to set the state of the navigation actions in
      * the toolbar.
      *
      */
     public abstract void setFocus();


     /**
      * Subclasses must implement to update the intro view actions when history
      * is updated.
      *
      */
     protected abstract void updateNavigationActionsState();



     public abstract boolean navigateBackward();

     public abstract boolean navigateForward();

     public abstract boolean navigateHome();


     /**
      * Called when the IntroPart is disposed. Subclasses should override to
      * dispose of resources. By default, this implementation does nothing.
      */
     public void dispose() {
         // no-op
 }


     /*
      * Add the Intro Model Viewer as an action to all implementations.
      */
     protected void addToolBarActions() {
         // Handle menus:
 IActionBars actionBars = getIntroPart().getIntroSite().getActionBars();
         IToolBarManager toolBarManager = actionBars.getToolBarManager();
         toolBarManager.add(viewIntroModelAction);
         toolBarManager.update(true);
         actionBars.updateActionBars();
     }

     /**
      * Called when the Intro changes state. This method should not be
      * subclassed. It adds performance logging calls. Subclasses must implement
      * doStandbyStateChanged instead.
      *
      * @param standby
      */
     public void standbyStateChanged(boolean standby, boolean isStandbyPartNeeded) {
         PerformanceStats setStandbyStateStats = null;
         long start = 0;
         if (Log.logPerformance) {
             if (logUIcreationTime && PerformanceStats.ENABLED) {
                 PerformanceStats stats = PerformanceStats.getStats(
                     IIntroConstants.PERF_UI_ZOOM, IIntroConstants.INTRO);
                 stats.endRun();
                 Util
                     .logPerformanceMessage(
                         "(perf stats) time spent in UI code before content is displayed (standbyStateChanged event is fired) ", //$NON-NLS-1$
 stats.getRunningTime());
                 stats.reset();
             }

             // standby time.
 setStandbyStateStats = PerformanceStats.getStats(
                 IIntroConstants.PERF_SET_STANDBY_STATE, IIntroConstants.INTRO);
             setStandbyStateStats.startRun();
             start = System.currentTimeMillis();
         }


         doStandbyStateChanged(standby, isStandbyPartNeeded);

         // now log performance
 if (Log.logPerformance) {
             if (PerformanceStats.ENABLED) {
                 setStandbyStateStats.endRun();
                 Util
                     .logPerformanceMessage(
                         "(perf stats) setting standby state (zooming, displaying content) took:", //$NON-NLS-1$
 +setStandbyStateStats.getRunningTime());
                 setStandbyStateStats.reset();
             } else
                 Util
                     .logPerformanceTime(
                         "setting standby state (zooming, generating content, setText() ) took:", //$NON-NLS-1$
 +start);

             if (logUIcreationTime) {
                 if (PerformanceStats.ENABLED) {
                     PerformanceStats stats = PerformanceStats.getStats(
                         IIntroConstants.PERF_VIEW_CREATION_TIME,
                         IIntroConstants.INTRO);
                     stats.endRun();
                     Util
                         .logPerformanceMessage(
                             "END - (perf stats): creating CustomizableIntroPart view took:", //$NON-NLS-1$
 +stats.getRunningTime());
                     stats.reset();
                 } else
                     Util.logPerformanceTime(
                         "END: creating CustomizableIntroPart view took:", //$NON-NLS-1$
 +IntroPlugin.getDefault().gettUICreationStartTime());


                 // prevent further logging of UI creation time.
 logUIcreationTime = false;
             }

         }
     }



     /*
      * Subclasses must implement the actual logic for the method.
      */
     protected abstract void doStandbyStateChanged(boolean standby,
             boolean isStandbyPartNeeded);


     /**
      * Save the current state of the intro. Currently, we only store information
      * about the most recently visited intro page. In static case, the last HTML
      * page is remembered. In dynamic case, the last UI page or HTML page is
      * remembered.
      *
      * Note: This method saves the last visited intro page in a dynamic case.
      * Subclasses need to extend to get the desired behavior relavent to the
      * specific implementation. Broswer implementation needs to cache an http
      * web page, if it happens to be the last page visited.
      *
      * @param memento
      */
     public void saveState(IMemento memento) {
         saveCurrentPage(memento);
     }


     /**
      * This method saves the most recently visited dynamic intro page in the
      * memento. If a given implementation requires saving alternative
      * information (e.g., information about the most recently visited static
      * page) it should override this method.
      *
      * @param memento
      */
     protected void saveCurrentPage(IMemento memento) {
         IntroModelRoot model = getModel();

         if (memento == null || model == null)
             return;
         String currentPage = model.getCurrentPageId();
         if (currentPage != null && currentPage.length() > 0) {
             memento.putString(IIntroConstants.MEMENTO_CURRENT_PAGE_ATT,
                 currentPage);
         }
     }


     /**
      * get the last page if it was stored in memento. This page is the last
      * visited intro page. It can be a intro page id, in the case of dynamic
      * intro. Or it can be an http in the case of static intro. It can also be
      * an http in the case of dynamic intro where the last visited page is a
      * url.
      */
     protected String getCachedCurrentPage() {
         // Check to see if the start page has been overriden because
 // content
 String newContentPage = ExtensionMap.getInstance().getStartPage();
         if (newContentPage != null) {
             return newContentPage;
         }
         IMemento memento = getMemento();
         if (memento == null)
             return null;
         return memento.getString(IIntroConstants.MEMENTO_CURRENT_PAGE_ATT);
     }


     /**
      * @return Returns the memento passed on creation.
      */
     public IMemento getMemento() {
         return memento;
     }

     /**
      * Support dynamic awarness. Clear cached models first, then update UI by
      * delegating to implementation.
      *
      * @see org.eclipse.core.runtime.IRegistryChangeListener#registryChanged(org.eclipse.core.runtime.IRegistryChangeEvent)
      */
     public void registryChanged(IRegistryChangeEvent event) {
         history.clear();
         // give implementation a chance to react to change.
 handleRegistryChanged(event);
     }

     /*
      * Handle reacting to plugin registry changes. This method will only be
      * called when regitry changes pertaining to Intro extension points is
      * detected.
      */
     protected abstract void handleRegistryChanged(IRegistryChangeEvent event);


     public History getHistory() {
         return history;
     }


 }

